/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.manager
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	import org.etherframework.common.DestructionPolicy;
	import org.etherframework.common.IDeferredDestroyable;
	import org.etherframework.common.IDestroyable;
	import org.etherframework.common.IManagedDestroyable;
	import org.etherframework.common.IStatefulDestroyable;
	import org.etherframework.common.IWeaklyDestroyable;

	public class DestructionManager
	{
		private var _dispatcher:Sprite;
		
		private static var _impl:DestructionManager;
		
		public function DestructionManager()
		{
			_dispatcher = new Sprite();
		}
		
		public static function getInstance():DestructionManager
		{
			if (!_impl)
				_impl = new DestructionManager();
			
			return _impl;
		}
		
		public function destroy(item:IDestroyable, destroyedHandler:Function = null):void
		{
			if (!item)
				return;
			
			if (item is IManagedDestroyable)
			{
				destroyManaged(IManagedDestroyable(item), destroyedHandler);
			}
			else
			{
				destroyDestroyable(item, destroyedHandler)
			}
		}
		
		private function destroyManaged(item:IManagedDestroyable, handler:Function):void
		{
			if (item.destructionPolicy == DestructionPolicy.AUTO)
			{
				destroyDestroyable(item, handler);
			}
			else if (item.destructionPolicy == DestructionPolicy.DETACH && 
				item is IWeaklyDestroyable)
			{
				destroyWeakly(IWeaklyDestroyable(item), handler);
			}
			else if (item.destructionPolicy == DestructionPolicy.DELAY && 
				item is IDeferredDestroyable)
			{
				destroyDeferred(IDeferredDestroyable(item), handler);
			}
			else if (handler is Function)
			{
				handler(item);
			}
		}
		
		private function destroyWeakly(item:IWeaklyDestroyable, handler:Function):void
		{
			if (item.destructionPolicy == DestructionPolicy.DETACH)
			{
				if (!item.detached) 
				{
					item.detach(); 
					item.detached = true;
				}
				if (handler is Function)
					handler(item);
			}
			else
			{
				destroyManaged(item, handler);
			}
		}
		
		private function destroyDeferred(item:IDeferredDestroyable, handler:Function):void
		{
			if (item.destructionPolicy == DestructionPolicy.DELAY)
			{
				if (!item.destructionDelay && item.destructionCondition)
				{
					destroyDestroyable(item, handler);
				}
				else if (item.destructionDelay)
				{
					deferDestroy(item, handler);
				}
			}
			else
			{
				destroyManaged(item, handler);
			}
		}
		
		private function destroyDestroyable(item:IDestroyable, handler:Function):void
		{
			if (item is IStatefulDestroyable && IStatefulDestroyable(item).destroyed)
				return;
			
			item.destroy();
			
			if (item is IStatefulDestroyable)
				IStatefulDestroyable(item).destroyed = true;
			
			if (handler is Function)
				handler(item);
		}
		
		private function deferDestroy(item:IDeferredDestroyable, handler:Function):void
		{
			var f:Function;
			var counter:int = item.destructionDelay;
			
			f = function(event:Event):void
			{
				if (!(--counter))
				{
					event.target.removeEventListener(Event.ENTER_FRAME, f);
					if (item.destructionCondition)
						destroyDestroyable(item, handler);
					else if (handler is Function)
						handler(item);
				}
			}
			_dispatcher.addEventListener(Event.ENTER_FRAME, f);
		}
	}
}